﻿using System;
using System.Diagnostics.Contracts;
using System.Runtime.Serialization;
using System.Security.Permissions;

namespace MathService.Values
{
    /// <summary>Усредняемое значение</summary>
    [Serializable]
    public class AverageValue : ISerializable, IValue<double>, IResetable
    {
        /* --------------------------------------------------------------------------------------------- */

        /// <summary>Номер итерации усреднения</summary>
        private int f_N;
        /// <summary>Текущее значение усредняемой величины</summary>
        private double f_Value;

        private bool f_StartEmpty;

        /* --------------------------------------------------------------------------------------------- */

        /// <summary>Размер окна усреднения</summary>
        public int Length { get; set; }

        /// <summary>Текущее значение усредняемой величины</summary>
        public double Value { get { return f_Value; } set { AddValue(value); } }

        ///<summary>
        ///</summary>
        public bool StartEmpty { get { return f_StartEmpty; } set { f_StartEmpty = value; } }

        /* --------------------------------------------------------------------------------------------- */

        /// <summary>Новая усредняемая величина</summary>
        /// <param name="Length">Размер окна усреднения</param>
        public AverageValue(int Length)
        {
            this.Length = Length;
            f_N = 0;
            f_Value = 0;
            f_StartEmpty = false;
        }
        /// <summary>Новая усредняемая величина</summary>
        /// <param name="StartValue">Начальное значение для усреднения</param>
        public AverageValue(double StartValue) : this(-1) { AddValue(StartValue); }
        /// <summary>Новая усредняемая величина</summary>
        /// <param name="StartValue">Начальное значение</param>
        /// <param name="Length">Размер окна усреднения</param>
        public AverageValue(double StartValue, int Length) : this(Length) { AddValue(StartValue); }


        /* --------------------------------------------------------------------------------------------- */

        /// <summary>Добавить значение к усреднению</summary>
        /// <param name="value">Добавляемое значение</param>
        public double AddValue(double value)
        {
            if(f_N < 1) { f_Value = value; f_N++; }
            else
                f_Value += (value - f_Value) / (f_StartEmpty
                    ? ((Length < 0 || f_N < Length ? f_N++ : f_N) + 1.0)
                    : Length);
            return f_Value;
        }

        public void Reset() { f_N = 0; }

        /* --------------------------------------------------------------------------------------------- */

        /// <summary>Преобразование в строку</summary>
        /// <returns>Текстовое представление</returns>
        public override string ToString() { return f_Value.ToString(); }

        /// <summary>Преобразование в строку с форматированием</summary>
        /// <param name="Format">Формат</param>
        /// <returns>Текстовое представление</returns>
        public string ToString(string Format)
        {
            return f_Value.ToString(Format);
        }

        /* --------------------------------------------------------------------------------------------- */

        public static implicit operator double(AverageValue Value) { return Value.Value; }
        public static implicit operator AverageValue(double Data) { return new AverageValue(Data); }

        /* --------------------------------------------------------------------------------------------- */

        #region ISerializable Members

        /// <summary>Новая усредняемая величина</summary>
        /// <param name="info">Сериализационная информация</param>
        /// <param name="context">Контекст сериализации</param>
        private AverageValue(SerializationInfo info, StreamingContext context)
        {
            Contract.Requires(info != null);
            if(info == null) throw new ArgumentNullException(nameof(info));
            f_Value = info.GetDouble("Value");
            f_N = info.GetInt32("N");
            Length = info.GetInt32("Length");
        }

        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
        {
            if(info == null) throw new ArgumentNullException(nameof(info));
            GetObjectData(info, context);
        }

        [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
        protected virtual void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            if(info == null) throw new ArgumentNullException(nameof(info));

            info.AddValue("Value", f_Value);
            info.AddValue("N", f_N);
            info.AddValue("Length", Length);
        }

        #endregion

        /* --------------------------------------------------------------------------------------------- */
    }
}
